package cn.fresh.xuanyuan.module.trade.service.order;

import cn.fresh.xuanyuan.framework.common.util.json.JsonUtils;
import cn.fresh.xuanyuan.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO;
import cn.fresh.xuanyuan.module.trade.controller.admin.order.vo.ExportOrderItemVO;
import cn.fresh.xuanyuan.module.trade.controller.admin.order.vo.TradeOrderItemBaseVO;
import cn.fresh.xuanyuan.module.trade.controller.admin.order.vo.TradeOrderPageReqVO;
import cn.fresh.xuanyuan.module.trade.controller.admin.order.vo.TradeOrderSummaryRespVO;
import cn.fresh.xuanyuan.module.trade.controller.app.order.vo.AppTradeOrderPageReqVO;
import cn.fresh.xuanyuan.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
import cn.fresh.xuanyuan.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.fresh.xuanyuan.module.trade.dal.dataobject.order.TradeOrderItemDO;
import cn.fresh.xuanyuan.module.trade.dal.mysql.order.TradeOrderItemMapper;
import cn.fresh.xuanyuan.module.trade.dal.mysql.order.TradeOrderMapper;
import cn.fresh.xuanyuan.module.trade.dal.redis.RedisKeyConstants;
import cn.fresh.xuanyuan.module.trade.dto.AdminQueryOrderItemDto;
import cn.fresh.xuanyuan.module.trade.framework.delivery.core.client.ExpressClientFactory;
import cn.fresh.xuanyuan.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
import cn.fresh.xuanyuan.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO;
import cn.fresh.xuanyuan.module.trade.service.delivery.DeliveryExpressService;
import cn.fresh.xuanyuan.module.trade.vo.OrderItemVo;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.fresh.xuanyuan.framework.common.pojo.PageResult;
import cn.fresh.xuanyuan.module.member.api.user.MemberUserApi;
import cn.fresh.xuanyuan.module.member.api.user.dto.MemberUserRespDTO;
import cn.fresh.xuanyuan.module.trade.enums.order.TradeOrderRefundStatusEnum;
import cn.fresh.xuanyuan.module.trade.enums.order.TradeOrderStatusEnum;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static cn.fresh.xuanyuan.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.fresh.xuanyuan.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.fresh.xuanyuan.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.fresh.xuanyuan.module.trade.enums.ErrorCodeConstants.EXPRESS_NOT_EXISTS;
import static cn.fresh.xuanyuan.module.trade.enums.ErrorCodeConstants.ORDER_NOT_FOUND;

/**
 * 交易订单【读】 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Slf4j
public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {

    @Resource
    private ExpressClientFactory expressClientFactory;

    @Resource
    private TradeOrderMapper tradeOrderMapper;
    @Resource
    private TradeOrderItemMapper tradeOrderItemMapper;

    @Resource
    private DeliveryExpressService deliveryExpressService;

    @Resource
    private MemberUserApi memberUserApi;

    // =================== Order ===================

    @Override
    public TradeOrderDO getOrder(Long id) {
        return tradeOrderMapper.selectById(id);
    }

    @Override
    public TradeOrderDO getOrder(Long userId, Long id) {
        TradeOrderDO order = tradeOrderMapper.selectById(id);
        if (order != null
                && ObjectUtil.notEqual(order.getUserId(), userId)) {
            return null;
        }
        return order;
    }

    @Override
    public TradeOrderDO getOrderByUserIdAndStatusAndCombination(Long userId, Long combinationActivityId, Integer status) {
        return tradeOrderMapper.selectByUserIdAndCombinationActivityIdAndStatus(userId, combinationActivityId, status);
    }

    @Override
    public List<TradeOrderDO> getOrderList(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return Collections.emptyList();
        }
        return tradeOrderMapper.selectBatchIds(ids);
    }

    @Override
    public PageResult<TradeOrderDO> getOrderPage(TradeOrderPageReqVO reqVO) {
        // 根据用户查询条件构建用户编号列表
        Set<Long> userIds = buildQueryConditionUserIds(reqVO);
        if (userIds == null) { // 没查询到用户，说明肯定也没他的订单
            return PageResult.empty();
        }
        // 分页查询
        return tradeOrderMapper.selectPage(reqVO, userIds);
    }

    private Set<Long> buildQueryConditionUserIds(TradeOrderPageReqVO reqVO) {
        // 获得 userId 相关的查询
        Set<Long> userIds = new HashSet<>();
        if (StrUtil.isNotEmpty(reqVO.getUserMobile())) {
            MemberUserRespDTO user = memberUserApi.getUserByMobile(reqVO.getUserMobile()).getCheckedData();
            if (user == null) { // 没查询到用户，说明肯定也没他的订单
                return null;
            }
            userIds.add(user.getId());
        }
        if (StrUtil.isNotEmpty(reqVO.getUserNickname())) {
            List<MemberUserRespDTO> users = memberUserApi.getUserListByNickname(reqVO.getUserNickname()).getCheckedData();
            if (CollUtil.isEmpty(users)) { // 没查询到用户，说明肯定也没他的订单
                return null;
            }
            userIds.addAll(convertSet(users, MemberUserRespDTO::getId));
        }
        return userIds;
    }

    @Override
    public TradeOrderSummaryRespVO getOrderSummary(TradeOrderPageReqVO reqVO) {
        // 根据用户查询条件构建用户编号列表
        Set<Long> userIds = buildQueryConditionUserIds(reqVO);
        if (userIds == null) { // 没查询到用户，说明肯定也没他的订单
            return new TradeOrderSummaryRespVO();
        }
        // 查询每个售后状态对应的数量、金额
        List<Map<String, Object>> list = tradeOrderMapper.selectOrderSummaryGroupByRefundStatus(reqVO, null);

        TradeOrderSummaryRespVO vo = new TradeOrderSummaryRespVO().setAfterSaleCount(0L).setAfterSalePrice(0L);
        for (Map<String, Object> map : list) {
            Long count = MapUtil.getLong(map, "count", 0L);
            Long price = MapUtil.getLong(map, "price", 0L);
            // 未退款的计入订单，部分退款、全部退款计入售后
            if (TradeOrderRefundStatusEnum.NONE.getStatus().equals(MapUtil.getInt(map, "refundStatus"))) {
                vo.setOrderCount(count).setOrderPayPrice(price);
            } else {
                vo.setAfterSaleCount(vo.getAfterSaleCount() + count).setAfterSalePrice(vo.getAfterSalePrice() + price);
            }
        }
        return vo;
    }

    @Override
    public PageResult<TradeOrderDO> getOrderPage(Long userId, AppTradeOrderPageReqVO reqVO) {
        return tradeOrderMapper.selectPage(reqVO, userId);
    }

    @Override
    public Long getOrderCount(Long userId, Integer status, Boolean commentStatus) {
        return tradeOrderMapper.selectCountByUserIdAndStatus(userId, status, commentStatus);
    }

    @Override
    public List<ExpressTrackRespDTO> getExpressTrackList(Long id, Long userId) {
        // 查询订单
        TradeOrderDO order = tradeOrderMapper.selectByIdAndUserId(id, userId);
        if (order == null) {
            throw exception(ORDER_NOT_FOUND);
        }
        // 查询物流
        return getExpressTrackList(order);
    }

    @Override
    public List<ExpressTrackRespDTO> getExpressTrackList(Long id) {
        // 查询订单
        TradeOrderDO order = tradeOrderMapper.selectById(id);
        if (order == null) {
            throw exception(ORDER_NOT_FOUND);
        }
        // 查询物流
        return getExpressTrackList(order);
    }

    @Override
    public int getSeckillProductCount(Long userId, Long activityId) {
        // 获得订单列表
        List<TradeOrderDO> orders = tradeOrderMapper.selectListByUserIdAndSeckillActivityId(userId, activityId);
        orders.removeIf(order -> TradeOrderStatusEnum.isCanceled(order.getStatus())); // 过滤掉【已取消】的订单
        if (CollUtil.isEmpty(orders)) {
            return 0;
        }
        // 获得订单项列表
        return tradeOrderItemMapper.selectProductSumByOrderId(convertSet(orders, TradeOrderDO::getId));
    }

    /**
     * 获得订单的物流轨迹
     *
     * @param order 订单
     * @return 物流轨迹
     */
    private List<ExpressTrackRespDTO> getExpressTrackList(TradeOrderDO order) {
        if (order.getLogisticsId() == null) {
            return Collections.emptyList();
        }
        // 查询物流公司
        DeliveryExpressDO express = deliveryExpressService.getDeliveryExpress(order.getLogisticsId());
        if (express == null) {
            throw exception(EXPRESS_NOT_EXISTS);
        }
        // 查询物流轨迹
        return getSelf().getExpressTrackList(express.getCode(), order.getLogisticsNo(), order.getReceiverMobile());
    }

    /**
     * 查询物流轨迹
     *
     * 缓存的目的：考虑及时性要求不高，但是每次调用需要钱
     *
     * @param code           快递公司编码
     * @param logisticsNo    发货快递单号
     * @param receiverMobile 收、寄件人的电话号码
     * @return 物流轨迹
     */
    @Cacheable(cacheNames = RedisKeyConstants.EXPRESS_TRACK, key = "#code + '-' + #logisticsNo + '-' + #receiverMobile",
            condition = "#result != null")
    public List<ExpressTrackRespDTO> getExpressTrackList(String code, String logisticsNo, String receiverMobile) {
        return expressClientFactory.getDefaultExpressClient().getExpressTrackList(
                new ExpressTrackQueryReqDTO().setExpressCode(code).setLogisticsNo(logisticsNo)
                        .setPhone(receiverMobile));
    }


    // =================== Order Item ===================

    @Override
    public TradeOrderItemDO getOrderItem(Long userId, Long itemId) {
        TradeOrderItemDO orderItem = tradeOrderItemMapper.selectById(itemId);
        if (orderItem != null
                && ObjectUtil.notEqual(orderItem.getUserId(), userId)) {
            return null;
        }
        return orderItem;
    }

    @Override
    public TradeOrderItemDO getOrderItem(Long id) {
        return tradeOrderItemMapper.selectById(id);
    }

    @Override
    public List<TradeOrderItemDO> getOrderItemListByOrderId(Collection<Long> orderIds) {
        if (CollUtil.isEmpty(orderIds)) {
            return Collections.emptyList();
        }
        return tradeOrderItemMapper.selectListByOrderId(orderIds);
    }

    @Override
    public List<OrderItemVo> getOrderList(Long userId, AppTradeOrderPageReqVO req) {
        List<OrderItemVo> data = new ArrayList<>();
        PageHelper.startPage(req.getPageNo(), req.getPageSize());
        List<Integer> statusList = getIntegers(req);
        if("null".equalsIgnoreCase(req.getKeyword()) || "undefined".equalsIgnoreCase(req.getKeyword())) {
            req.setKeyword(null);
        }
        List<TradeOrderDO> orderList = tradeOrderMapper.myOrderList(userId, statusList);

        if(CollectionUtil.isNotEmpty(orderList)) {
            for(TradeOrderDO order : orderList) {
                List<TradeOrderItemBaseVO> list = tradeOrderItemMapper.getItemList(order.getId());
                if(CollectionUtil.isNotEmpty(list)) {
                    if(order.getStatus() == null
                            || Objects.equals(order.getStatus(), TradeOrderStatusEnum.UNPAID.getStatus())
                            || Objects.equals(order.getStatus(), TradeOrderStatusEnum.CANCELED.getStatus())) {
                        List<OrderItemVo> items = buildNotPayOrderItems(list, order.getStatus(), order.getNo());
                        data.addAll(items);
                    }
                    else {
                        List<OrderItemVo> res2 = buildPaidOrderItems(list);
                        data.addAll(res2);
                    }
                }
            }
        }

        return data;
    }

    @Override
    public PageResult<TradeOrderItemBaseVO> getOrderItemList(AdminQueryOrderItemDto query) {
        Page<TradeOrderItemBaseVO> page = PageHelper.startPage(query.getPageNo(), query.getPageSize());
        List<TradeOrderItemBaseVO> list = tradeOrderItemMapper.pageList(query);
        PageResult<TradeOrderItemBaseVO> result = new PageResult<>(list, page.getTotal());
        return result;
    }

    @Override
    public List<ExportOrderItemVO> exportOrderItems(AdminQueryOrderItemDto query) {
        List<TradeOrderItemBaseVO> list = tradeOrderItemMapper.pageList(query);
        List<ExportOrderItemVO> data = buildExport(list);
        return data;
    }

    @Override
    public TradeOrderItemBaseVO getOrderItemDetail(Long id) {
        TradeOrderItemDO orderItemDO = tradeOrderItemMapper.selectById(id);
        TradeOrderDO orderDO = tradeOrderMapper.selectById(orderItemDO.getOrderId());
        TradeOrderItemBaseVO detail = new TradeOrderItemBaseVO();
        detail.setId(orderItemDO.getId());
        detail.setNo(orderDO.getNo());
        detail.setOrderItemNo(orderItemDO.getOrderItemNo());
        detail.setExpressName(orderItemDO.getExpressName());
        detail.setLogisticsNo(orderItemDO.getLogisticsNo());
        detail.setPicUrl(orderItemDO.getPicUrl());
        detail.setSpuName(orderItemDO.getSpuName());
        detail.setSpuId(orderItemDO.getSpuId());
        detail.setUserId(orderDO.getUserId());
        detail.setOrchardId(orderItemDO.getOrchardId());
        detail.setOrchardName(orderItemDO.getOrchardName());
        detail.setReceiverName(orderDO.getReceiverName());
        detail.setReceiverDetailAddress(orderDO.getReceiverDetailAddress());
        detail.setReceiverMobile(orderDO.getReceiverMobile());
        detail.setStatus(orderItemDO.getStatus());
        detail.setCount(orderItemDO.getCount());
        detail.setRemark(orderDO.getRemark());
        detail.setRefundStatus(orderItemDO.getRefundStatus());
        detail.setProperties(JsonUtils.parseArray(JSON.toJSONString(orderItemDO.getProperties()), ProductPropertyValueDetailRespVO.class));
        return detail;
    }

    private List<ExportOrderItemVO> buildExport(List<TradeOrderItemBaseVO> list) {
        List<ExportOrderItemVO> data = new ArrayList<>();
        if(CollectionUtil.isNotEmpty(list)) {
            for(TradeOrderItemBaseVO v : list) {
                ExportOrderItemVO o = ExportOrderItemVO.builder()
                        .orderItemNo(v.getOrderItemNo())
                        .spuName(v.getSpuName())
                        .build();
                if(CollectionUtil.isNotEmpty(v.getProperties())) {
                    StringBuffer sbf = new StringBuffer();
                    for(ProductPropertyValueDetailRespVO p : v.getProperties()) {
                        sbf.append(p.getPropertyName()).append("：").append(p.getValueName());
                    }
                    o.setSku(sbf.toString());
                }
                StringBuffer receiver = new StringBuffer(v.getReceiverName());
                receiver.append("  ").append(v.getReceiverMobile()).append("  ").append(v.getReceiverDetailAddress());
                o.setReceiver(receiver.toString());
                data.add(o);
            }
        }
        return data;
    }

    @NotNull
    private static List<Integer> getIntegers(AppTradeOrderPageReqVO req) {
        List<Integer> statusList = new ArrayList<>();
        if(req.getStatus() != null) {
            if(req.getStatus() == 0) {
                statusList.add(TradeOrderStatusEnum.UNPAID.getStatus());
            }
            if(req.getStatus() == 1) {
                statusList.add(TradeOrderStatusEnum.UNDELIVERED.getStatus());
                statusList.add(TradeOrderStatusEnum.DELIVERED.getStatus());
            }
            if(req.getStatus() == 2) {
                statusList.add(TradeOrderStatusEnum.COMPLETED.getStatus());
            }
            if(req.getStatus() == 3) {
                statusList.add(TradeOrderStatusEnum.CANCELED.getStatus());
            }
        }
        return statusList;
    }

    /**
     * 构建已经支付的订单
     * @param otherList 已支付列表
     * @return result
     */
    private List<OrderItemVo> buildPaidOrderItems(List<TradeOrderItemBaseVO> otherList) {
        List<OrderItemVo> result = new ArrayList<>();
        if(CollectionUtil.isNotEmpty(otherList)) {
            for(TradeOrderItemBaseVO item : otherList) {
                OrderItemVo vo = OrderItemVo.builder()
                        .orderItemId(item.getId())
                        .orderId(item.getOrderId())
                        .status(item.getStatus())
                        .isPay(true)
                        .no(item.getNo())
                        .afterSaleStatus(item.getAfterSaleStatus())
                        .payPrice(item.getPayPrice())
                        .orderItemId(item.getId())
                        .orderItemNo(item.getOrderItemNo())
                        .createTime(item.getCreateTime())
                        .spuImage(item.getPicUrl())
                        .spuName(item.getSpuName())
                        .orchardId(item.getOrchardId())
                        .count(item.getCount())
                        .spuId(item.getSpuId())
                        .orchardName(item.getOrchardName())
                        .build();
                String sku = getSku(item.getProperties());
                vo.setSku(sku);
                result.add(vo);
            }
        }
        return result;
    }

    private String getSku(List<ProductPropertyValueDetailRespVO> properties) {
        StringBuilder sbf = new StringBuilder();
        for(ProductPropertyValueDetailRespVO p : properties) {
            sbf.append(p.getValueName()).append("、");
        }
        String sku = sbf.toString();
        if(sku.endsWith("、")) {
            sku = sku.substring(0, sku.length() - 1);
        }
        return sku;
    }

    /**
     * 构建未支付订单信息
     * @param notPayList 未支付列表
     * @param status 状态
     * @return result
     */
    private List<OrderItemVo> buildNotPayOrderItems(List<TradeOrderItemBaseVO> notPayList, Integer status, String no) {
        List<OrderItemVo> result = new ArrayList<>();
        List<String> images = new ArrayList<>();
        int payPrice = 0;
        int total = 0;
        int num = 0;
        for(TradeOrderItemBaseVO o : notPayList) {
            if(num < 3) {
                images.add(o.getPicUrl());
            }
            num++;
            if(o.getPayPrice() != null) {
                payPrice += o.getPayPrice();
            }
            total += o.getCount();
        }
        TradeOrderItemBaseVO vo = notPayList.get(0);
        String spuName = vo.getSpuName() + "……";
        OrderItemVo item = OrderItemVo.builder()
                .isPay(false)
                .orderId(vo.getOrderId())
                .spuName(spuName)
                .no(no)
                .payPrice(payPrice)
                .count(total)
                .spuImages(images)
                .status(status)
                .build();
        result.add(item);
        return result;
    }

    /**
     * 获得自身的代理对象，解决 AOP 生效问题
     *
     * @return 自己
     */
    private TradeOrderQueryServiceImpl getSelf() {
        return SpringUtil.getBean(getClass());
    }

}
